/**
 * Copyright (c) 2018, Alex. All rights reserved.
 */
package com.oschina.bottle.coder.symmetric;

import com.oschina.bottle.coder.CoderException;
import com.oschina.bottle.common.constants.Charset;
import java.io.UnsupportedEncodingException;
import java.security.Key;
import java.security.SecureRandom;
import java.util.Base64;
import java.util.Base64.Decoder;
import java.util.Base64.Encoder;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

/**
 * 对称加密算法
 *
 * ++++ 采用Base64将byte数组转换为可读的字符串
 * ++++ Base64提供默认的code表 和 url code表。分别适应在不同的场景中
 * ++++ 如加密后的串需要在浏览器地址中传递，则需要用url code表。其他情况可以无脑选择默认的code表
 *
 * 在加解密方法中，会根据mode进行判断
 * ++++ encrypt(String data, String password, int mode) decrypt(String data, String password, int mode)
 *
 * @author <a href="mailto:zhongchao@gegejia.com">zhong</a>
 * @version 1.0 2018/1/4
 * @since 1.0
 */
public class SymmetricCoder {

    private SymmetricAlgorithm algorithm;

    private String key;

    /**
     * 默认编码 解码格式
     */
    public final static int BASE64_ENCODE_DEFAULT = 1;

    /**
     * URL默认编码 解码格式，用于web连接
     */
    public final static int BASE64_ENCODE_URL = 2;

    public SymmetricCoder(SymmetricAlgorithm algorithm, String key) {
        if(key == null || "".equals(key)) {
            throw new IllegalArgumentException("the key must be empty");
        }
        this.algorithm = algorithm;
        this.key = key;
    }

    private byte[] initKey() throws Exception {
        KeyGenerator generator = KeyGenerator.getInstance(algorithm.getName());

        //初始化密钥生成器
        SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
        secureRandom.setSeed(key.getBytes());
        generator.init(algorithm.getLength(), secureRandom);

        //生成密钥
        SecretKey secretKey = generator.generateKey();

        //获取二进制密钥
        return secretKey.getEncoded();
    }

    private Key toKey() throws Exception {
        return new SecretKeySpec(initKey(), algorithm.getName());
    }

    /**
     * 创建密码器
     */
    private Cipher initCipher(int mode) throws Exception {
        //获得AES密钥
        Key key = toKey();

        //创建密码器
        Cipher cipher = Cipher.getInstance(algorithm.getName());

        //初始化，设置为加密模式
        cipher.init(mode, key);

        return cipher;
    }

    public byte[] encrypt(byte[] data) {

        try {
            Cipher cipher = initCipher(Cipher.ENCRYPT_MODE);
            return cipher.doFinal(data);
        } catch (Exception e) {
            throw new CoderException(e, "加密失败");
        }
    }

    public byte[] decrypt(byte[] data) {
        try {
            Cipher cipher = initCipher(Cipher.DECRYPT_MODE);
            return cipher.doFinal(data);
        } catch (Exception e) {
            throw new CoderException(e, "解密失败");
        }
    }

    public String encrypt(String data, int mode) {
        byte[] dataBytes = Charset.formatUTF8(data);
        Encoder encoder = mode == BASE64_ENCODE_URL ? Base64.getUrlEncoder() : Base64.getEncoder();
        return encoder.encodeToString(encrypt(dataBytes));
    }

    public String decrypt(String data, int mode) {
        validateMode(mode);

        Decoder decoder = mode == BASE64_ENCODE_URL ? Base64.getUrlDecoder() : Base64.getDecoder();
        byte[] dataBytes = decoder.decode(data);

        return new String(decrypt(dataBytes));
    }

    private void validateMode(int mode) {
        if (mode != BASE64_ENCODE_DEFAULT && mode != BASE64_ENCODE_URL) {
            throw new CoderException("the mode must int (1, 2)");
        }
    }
}
